#include "ucdinputfile.h"
#include "GL/glut.h"
#include "model.h"
#include "visRayCasting.h"

#include <vector>

using namespace visualization;
const int imagesize = 350;
const int smpr = 250;

/*********The following draws the diagram needed using Opengl**********/
// This varaible specifys the segment number of the range of the data.
static const int seg = 500;
// Record the number of data point at the corrsponding value.
vector<double> num_of_point;
Model modl;

// Sometimes the histogram is too low. So it should be magnified.
double magnify;

// The dimensions are seg+1 but not seg, because that the variable 
// num_of_point has seg+1 points.
// And the 3 indicates RGB.
GLubyte _image[seg + 1][seg + 1][3];

void setMagnify(double d = 50)
{
	magnify = d;
}
void makeImage(void)
{
	int i, j;//, c;
	// Initializatoin.
	for(i = 0; i <= seg; i++)
	{
		for(j = 0; j <= seg; j++)
		{
			for(int k = 0; k < 3; k++)
			{
				_image[i][j][k] = 180;
			}
		}
	}
}


void init(void)
{
	visRayCasting smpPoints(&modl);
	smpPoints.generateTransferFunction();
	smpPoints.setImageSize(imagesize);
	smpPoints.setSamplingRate(smpr);
	double direction[] = {100, 0, 129};
	smpPoints.setViewingPosition(direction, 3);
	smpPoints.resample();

	double *result = smpPoints.getPointsValue();
	//////////////////////////////////////////////////////////////

	// The range of the data.
	double dataLow, dataHigh;

	if(! modl.getDataValueRange(dataLow, dataHigh) )
	{
		exit(-1);
	}

	cout << "dataLow: " << dataLow << endl;
	cout << "dataHigh: " << dataHigh << endl;
	double interval = (dataHigh - dataLow) / seg;
	
	// Note that here "i <= seg".
	for(int i = 0; i <= seg; i++)
	{
		num_of_point.push_back(0);
	}
//	double highest = -1;
	double usedP = 0;
	int index;
	std::vector<double> data;
	for(unsigned int i = 1; i <= modl.getPntNumberOfModel(); i++)
	{
		modl.getDataValueOfPoint(i, data);
		// Here a simple approach is employed to determine which
		// segment the data should belong to.
		// Note that another "interval/2" is added.
		usedP++;
		index = (data[0] - dataLow + interval / 2) / interval;
		num_of_point[index] += 1;
	}

	for(int i = 0; i < imagesize * imagesize * smpr; i++)
	{
		usedP++;
		index = (result[i] - dataLow + interval / 2) / interval;
		num_of_point[index] += 1;
	}
	double tmp = magnify * seg/( (double)usedP );
	// Normalize the number to let them fall between 0 and 1.
	for(unsigned int i = 0; i < num_of_point.size(); i++)
	{
		num_of_point[i] *= tmp;
	}

	glClearColor(1.0, 1.0, 1.0, 0.0);
	glShadeModel(GL_FLAT);
	makeImage();
	// Assign the result to _image.
	for(int j = 0; j <= seg; j++)
	{
		for(int i = 0; i <= num_of_point[j] && i <= seg; i++)
		{
			for(int k = 0; k < 3; k++)
			{
				_image[i][j][k] = 0;
			}
		}
	}
	
	// Draw some scales.
	const int squareLen = 8;
	const int scalePointNum = 5;
	for(int i = 0; i < squareLen; i++)
	{
		for(int j = 0; j <= seg; j += seg/scalePointNum)
		{
			for(int k = j; k < j + squareLen; k++)
			{
				_image[i][k][0] = 0;
				_image[i][k][1] = 100;
				_image[i][k][2] = 200;
			}
		}
	}
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
	glRasterPos2f(-0.97, -0.97);
	glDrawPixels(seg + 1, seg + 1, GL_RGB, GL_UNSIGNED_BYTE, _image);
	glFlush();
}
void reshape (int w, int h)
{
	glViewport (0, 0, (GLsizei) w, (GLsizei) h);
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ( );
	if (w <= h)
	{
		glOrtho(-1.5, 1.5, -1.5 * ( GLfloat ) h / ( GLfloat ) w,
				1.5 * ( GLfloat ) h / ( GLfloat ) w, -10.0, 10.0 );
	}
	else
	{
		glOrtho(-1.5 * ( GLfloat ) w / ( GLfloat ) h,
				1.5 * ( GLfloat ) w / ( GLfloat ) h, -1.5, 1.5, -10.0, 10.0);
	}
	glMatrixMode ( GL_MODELVIEW );
	glLoadIdentity ( ) ;
}
void keyboard ( unsigned char key, int x, int y)
{
	switch (key) 
	{
		case 27:
			exit ( 0 );
			break;
	}
}
int main(int argc, char** argv)
{
	if(argc != 2 && argc != 3)
	{
		return 0;
	}
	UCDInputFile file(argv[1]);

	file.FGetModel(modl);
	modl.buildKDTree();

	setMagnify(500);
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800, 800);
	glutInitWindowPosition(100, 100);
	glutCreateWindow(argv[0]);
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMainLoop();

	return 0;
}
